package theorie;
import java.util.Arrays;
import java.util.Iterator;

/**Speichert eine Sequenz von Tokens, wie sie auf der linken oder
 * rechten Seite von Produktionen vorkommen. Die Tokens müssen
 * Strings sein, die automatisch interniert werden.
 *
 * @author franz
 */
public class TokenSequence implements Iterable<String>{
  String[] tokens;

  public TokenSequence(String... ts){
    tokens=new String[ts.length];
    for(int i=ts.length-1; i>=0; --i) tokens[i]=ts[i].intern();
  }

  @Override
  public Iterator<String> iterator(){
    return new Iterator<String>(){
      int i=0;

      @Override
      public boolean hasNext(){return i<size();}

      @Override
      public String next(){return tokens[i++];}

      @Override
      public void remove(){
        throw new UnsupportedOperationException("Don't try to remove tokens!");
      }
    };
  }

  /**Zwei TokenSequences sind gleich, wenn Sie dieselben (internierten)
   * Tokens in derselben Reihenfolge enthalten.
   * @param obj das zu vergleichende Objekt.
   * @return true genau dann, wenn obj eine TokenSequence ist und obiges
   *   zutrifft.
   */
  @Override
  public boolean equals(Object obj){
    if(!(obj instanceof TokenSequence)) return false;
    return Arrays.equals(((TokenSequence)obj).tokens, tokens);
  }

  @Override
  public int hashCode(){
    return 7*71+Arrays.deepHashCode(this.tokens);
  }

  /**Sucht part in dieser TokenSequence ab der Position from.
   * @param part die zu suchenden Tokens.
   * @param from die Startposition
   * @return die Position, ab der part gefunden wurde oder -1, falls nicht.
   */
  public int indexOf(TokenSequence part, int from){
    if(from<0) from=0;
    int parthinten;
    while(from+(parthinten=part.size()-1)<size()){
      while(parthinten>=0 && tokens[from+parthinten].equals(part.tokens[parthinten]))
        parthinten--;
      if(parthinten==0) return from;
      else from++;
    }
    return -1;
  }

  public int indexOf(TokenSequence part){
    return indexOf(part, 0);
  }

  /**Testet, ob ein String als Token in dieser TokenSequence enthalten ist.
   * @param s der zu testende String.
   * @param from die Startposition, ab der getestet wird.
   * @return true genau dann, wenn s enthalten ist.
   */
  public int indexOf(String s, int from){
    for(int n=size(); from<n; ++from)
      if(s.equals(tokens[from])) return from;
    return -1;
  }

  public int indexOf(String s){
    return indexOf(s, 0);
  }

  public int size(){return tokens.length;}

  /**Ersetzt einen Teil der TokenSequence durch eine andere.
   * 
   * @param neu die neue TokenSequence.
   * @param from die Position des ersten zu ersetzenden Tokens.
   * @param to die Position nach dem letzten zu ersetzenden Token.
   * @return eine neue TokenSequence oder die alte, falls nichts zu tun war.
   */
  public TokenSequence replaceBy(TokenSequence neu, int from, int to){
    if(from<0) from=0;
    if(to>size()) to=size();
    if(from>=to) return this;
    String[] toks=new String[size()-(to-from)+neu.size()];
    int j=0;
    for(int i=0; i<from; ++i) toks[j++]=tokens[i];
    for(String s:neu) toks[j++]=s;
    for(int i=to, n=size(); i<n; ++i) toks[j++]=tokens[i];
    return new TokenSequence(toks);
  }

  @Override
  public String toString(){
    StringBuffer sb=new StringBuffer("´");
    for(int i=0; i<size(); ++i){
      sb.append(tokens[i]);
      sb.append("´");
    }
    return sb.toString();
  }
}